희소(Sparse) 복셀 그리드 구조의 이해
로봇이 미지의 환경을 탐사하고 이해하는 과정에서 가장 근본적인 질문은 “공간을 어떻게 표현할 것인가?“이다. 이 질문에 대한 해답은 로봇의 인지 능력, 경로 계획의 효율성, 그리고 전체 시스템의 반응 속도를 결정짓는 핵심 변수가 된다. NvBlox는 NVIDIA의 GPU 가속 기술을 로보틱스 매핑 분야에 접목시킨 최첨단 라이브러리로, 그 설계의 중심에는 **희소 복셀 그리드(Sparse Voxel Grid)**라는 정교한 데이터 구조가 자리 잡고 있다. 이 절에서는 NvBlox가 채택한 희소 복셀 그리드의 설계 철학부터 메모리 계층 구조, GPU 하드웨어와의 상호작용, 그리고 이를 통해 달성되는 성능 최적화의 원리까지, 그 기술적 내막을 바닥부터 끝까지 해부한다. 단순한 기능 설명이 아닌, 왜 이러한 구조가 필연적이었는지에 대한 공학적 타당성과 그로 인한 파급 효과를 심도 있게 분석한다.
1. 서론: 공간 표현의 딜레마와 희소성의 필연성
3차원 공간을 디지털 메모리 상에 투영하는 것은 본질적으로 타협의 산물이다. 무한한 연속 공간(Continuous Space)을 유한한 이산 공간(Discrete Space)으로 변환해야 하기 때문이다. 초기 로봇 공학에서는 단순하고 직관적인 밀집 그리드(Dense Grid) 방식을 사용했다. 이는 공간을 균일한 격자(Voxel)로 나누고 모든 격자에 데이터를 할당하는 방식이다. 10m \times 10m \times 3m 크기의 방을 1cm 단위의 복셀로 표현한다고 가정해보자.
1000 \times 1000 \times 300 = 300,000,000 \text{ voxels}
단순히 공간의 점유 여부(Occupancy)만을 저장한다 해도 3억 개의 복셀이 필요하며, 여기에 거리 정보(Distance), 색상(Color), 의미론적 정보(Semantics)까지 포함하면 기가바이트(GB) 단위의 메모리가 순식간에 소모된다. 공간의 크기가 선형적으로 증가할 때 메모리 요구량은 세제곱(O(N^3))으로 폭증하는 이른바 ‘차원의 저주(Curse of Dimensionality)’ 현상이다.
그러나 실제 로봇이 주행하는 물리적 환경을 관찰해보면 중요한 통찰을 얻을 수 있다. 우리가 생활하는 공간의 대부분은 ’빈 공기(Free Space)’이며, 정보로서 가치가 있는 ’표면(Surface)’은 전체 부피의 극히 일부에 불과하다. 즉, 3차원 공간 데이터는 본질적으로 **희소(Sparse)**하다. 밀집 그리드 방식은 90% 이상의 불필요한 빈 공간을 저장하기 위해 메모리를 낭비하는 비효율적인 구조다.
이러한 비효율을 극복하기 위해 등장한 것이 옥트리(Octree) 구조다. OctoMap으로 대표되는 이 방식은 공간을 재귀적으로 8분할하여, 정보가 존재하는 영역에만 노드를 생성한다. 메모리 효율성 면에서는 획기적이지만, 트리 구조 특유의 부모-자식 노드 탐색 과정(Pointer Chasing)은 데이터 접근 시 불규칙한 메모리 점프를 유발한다. 이는 직렬 처리에 강한 CPU에서는 수용 가능하지만, 수천 개의 코어가 동시에 메모리에 접근해야 하는 GPU 환경에서는 치명적인 병목 현상을 일으킨다.1
NvBlox는 이러한 딜레마를 해결하기 위해 블록 해싱(Block Hashing) 기반의 희소 복셀 그리드 구조를 채택했다. 이는 전체 공간을 작은 블록(Block) 단위로 나누고, 데이터가 실제 존재하는 블록만을 해시 테이블(Hash Table)을 통해 관리하는 방식이다. 이 구조는 밀집 그리드의 빠른 인덱싱 성능(O(1))과 옥트리의 메모리 효율성을 동시에 달성하며, 무엇보다 GPU의 병렬 아키텍처에 완벽하게 부합하도록 설계되었다.
2. NvBlox의 2계층 메모리 계층 구조 (Two-Level Hierarchy)
NvBlox의 데이터 구조는 크게 두 가지 레벨로 나뉜다. 첫 번째 레벨은 광활한 3차원 공간 상에서 유의미한 데이터가 있는 위치를 빠르게 찾아내는 **GPU 해시 테이블(GPU Hash Table)**이며, 두 번째 레벨은 실제 물리적 데이터를 고밀도로 저장하고 있는 **복셀 블록(Voxel Block)**이다.3 이 2계층 구조(Two-Level Hierarchy)는 NvBlox가 자랑하는 고성능의 비밀이자, 대규모 환경에서도 실시간성을 유지할 수 있는 기반이다.
2.1 1레벨: GPU 가속 해시 테이블 (GPU-Accelerated Hash Table)
NvBlox의 근간이 되는 첫 번째 계층은 stdgpu 라이브러리를 기반으로 최적화된 개방형 주소 지정(Open Addressing) 방식의 해시 테이블이다.3 이 해시 테이블은 3차원 공간의 정수 좌표 인덱스 (x, y, z)를 키(Key)로 받아, 해당 위치에 대응하는 복셀 블록의 메모리 포인터 또는 인덱스를 값(Value)으로 반환한다.
2.1.1 공간 해싱(Spatial Hashing)의 수학적 원리
전역 좌표계(Global Coordinate)상의 특정 위치 P(p_x, p_y, p_z)가 주어졌을 때, 이를 포함하는 블록의 인덱스 I(i_x, i_y, i_z)는 다음과 같이 계산된다.
i_x = \lfloor p_x / B \rfloor, \quad i_y = \lfloor p_y / B \rfloor, \quad i_z = \lfloor p_z / B \rfloor
여기서 B는 블록의 물리적 크기(미터 단위)를 의미한다. 예를 들어 블록 크기가 0.4m라면, 좌표 (1.0, 0.5, 0.1)은 블록 인덱스 (2, 1, 0)에 해당한다. 이렇게 얻어진 3차원 정수 벡터 I는 해시 함수 H(I)를 통해 1차원 해시 키로 변환되어 테이블 내의 버킷(Bucket) 주소를 결정한다. NvBlox는 GPU에서의 비트 연산 효율성을 극대화하고 충돌을 최소화하기 위해 최적화된 공간 해시 함수를 사용한다. 일반적인 형태는 다음과 같은 큰 소수(Large Prime Numbers)를 이용한 선형 결합과 XOR 연산의 조합이다.
H(i_x, i_y, i_z) = ((i_x \cdot P_1) \oplus (i_y \cdot P_2) \oplus (i_z \cdot P_3)) \mod M
여기서 \oplus는 XOR 연산, P_1, P_2, P_3는 서로 다른 큰 소수, M은 해시 테이블의 전체 버킷 크기를 나타낸다. 이러한 해시 함수는 인접한 블록들이 해시 테이블 상에서는 서로 멀리 떨어진 위치에 분산되도록 유도(Avalanche Effect)하여, 특정 영역에 데이터가 몰릴 때 발생할 수 있는 클러스터링(Clustering) 현상을 방지한다.
2.1.2 GPU 환경에서의 충돌 해결(Collision Resolution)과 선형 탐사
해시 테이블의 영원한 숙제인 해시 충돌(Hash Collision)—서로 다른 블록 인덱스가 우연히 동일한 해시 값을 가지는 현상—은 수천 개의 스레드가 동시에 접근하는 GPU 환경에서 더욱 복잡한 문제가 된다. 일반적인 CPU 해시 테이블에서 사용하는 체이닝(Chaining, 연결 리스트를 이용한 방식)은 동적 메모리 할당과 포인터 연결을 필요로 하므로 GPU에서는 성능상 최악의 선택이다.
NvBlox는 이에 대한 해결책으로 선형 탐사(Linear Probing) 방식을 채택했다.6 선형 탐사 방식에서는 충돌이 발생할 경우, 정해진 보폭(Stride)만큼 인덱스를 증가시키며 다음 빈 버킷을 찾는다.
\text{Index}_{next} = (\text{Index}_{current} + 1) \mod M
이 방식의 장점은 메모리 접근 패턴을 단순화하고 지역성(Locality)을 유지할 수 있다는 점이다. GPU는 메모리를 블록 단위로 읽어오기 때문에, 충돌이 발생하여 바로 옆 칸을 조회할 때 해당 데이터가 이미 L2 캐시에 있을 확률이 높다. 또한, stdgpu 라이브러리는 CUDA의 원자적 연산(Atomic Operations, 예: atomicCAS)을 사용하여 멀티 스레드 환경에서의 데이터 경쟁(Race Condition) 없이 안전하게 키를 삽입하고 조회하는 메커니즘을 제공한다.5 이는 수만 개의 복셀 업데이트가 동시에 일어나는 상황에서도 데이터 무결성을 보장하는 핵심 기술이다.
2.1.3 부하율(Load Factor) 관리와 동적 리사이징 (Dynamic Resizing)
해시 테이블의 성능은 데이터가 채워진 비율인 부하율에 절대적으로 의존한다. 부하율이 50-70%를 넘어가면 충돌 빈도가 급격히 증가하고, 선형 탐사의 길이가 길어져 O(1)이어야 할 조회 성능이 O(N)에 가깝게 저하된다. 이는 초당 30프레임 이상의 실시간 처리를 요구하는 로봇 매핑에서는 허용될 수 없는 지연이다.
NvBlox는 이를 방지하기 위해 런타임 중에 해시 테이블의 부하율을 지속적으로 모니터링한다. 만약 임계치(일반적으로 0.5~0.7)를 초과할 경우, 시스템은 즉시 리사이징(Resizing) 작업을 수행한다.7 리사이징 과정은 다음과 같이 진행된다:
- 메모리 확장: 기존 크기의 2배(또는 설정된 비율)에 해당하는 새로운 메모리 공간을 GPU 글로벌 메모리에 할당한다. 로그 상에서는
Resizing GPU hash capacity from 4096 to 8192와 같은 형태로 관측된다. - 재해싱(Rehashing): 새로운 해시 함수(모듈로 연산의 분모 M이 변경됨)를 적용하여 기존 테이블의 모든 키-값 쌍을 새로운 테이블로 이동시킨다. 이 과정은 대규모의 병렬 커널을 통해 수행되므로 CPU 방식보다 훨씬 빠르지만, 여전히 비용이 드는 작업이다.
- 메모리 해제: 기존의 작은 테이블 메모리를 해제하여 힙(Heap)으로 반환한다.
이러한 동적 확장성은 NvBlox가 작은 방 하나부터 거대한 창고나 야외 환경까지, 메모리가 허용하는 한 중단 없이 매핑을 지속할 수 있게 하는 원동력이다. 사용자는 초기 크기를 작게 설정하더라도 시스템이 알아서 확장하므로 복잡한 튜닝 없이 바로 사용할 수 있다.
2.2 2레벨: 복셀 블록 (Voxel Block) - 데이터의 컨테이너
해시 테이블이 ’지도상의 주소’를 알려주는 내비게이션이라면, 실제 ’그곳에 무엇이 있는지’를 담고 있는 건물은 바로 복셀 블록이다. NvBlox에서 하나의 블록은 8 \times 8 \times 8 개의 복셀(Voxel)로 구성된 3차원 배열이다.3
2.2.1 왜 하필 8x8x8인가? GPU 하드웨어와의 정렬
총 512개의 복셀로 구성된 이 블록 크기는 임의로 정해진 것이 아니라, NVIDIA GPU 아키텍처의 특성을 철저히 고려한 공학적 결정이다.
- 워프(Warp) 실행 모델과의 조화: NVIDIA GPU는 32개의 스레드를 하나의 워프(Warp)로 묶어 동시에 같은 명령어를 실행한다(SIMT 구조). 512개의 복셀은 32 \times 16이므로, 16개의 워프가 블록 하나를 처리하거나, 하나의 워프가 루프를 통해 처리하기에 완벽하게 떨어지는 숫자다. 만약 블록 크기가 10 \times 10 \times 10이었다면 1000개의 복셀이 되어 32로 나누어 떨어지지 않고, 남는 스레드(Idle Threads)가 발생하거나 복잡한 경계 처리가 필요했을 것이다.
- 캐시 지역성(Cache Locality): 8 \times 8 \times 8 크기의 데이터 덩어리는 GPU의 L1/L2 캐시 라인에 적재되기에 매우 적절한 크기다. 너무 작은 블록(예: 4 \times 4 \times 4)은 블록 헤더 오버헤드를 증가시키고 해시 테이블 조회를 너무 빈번하게 만든다. 반면 너무 큰 블록(예: 16 \times 16 \times 16)은 내부의 많은 복셀이 비어있음에도 메모리를 차지하게 되어 희소성의 이점을 희석시키고, 캐시 미스(Cache Miss)를 유발할 수 있다.
- 메모리 코알레싱(Memory Coalescing): 가장 중요한 이유는 메모리 접근의 효율성이다. 블록 내부의 복셀들은 메모리상에 연속적으로 저장된다. 이는 인접한 스레드들이 인접한 메모리 주소에 접근할 때, GPU 메모리 컨트롤러가 이를 하나의 트랜잭션으로 병합(Coalesce)하여 처리할 수 있게 한다.
2.2.2 메모리 레이아웃: SoA (Structure of Arrays) vs. AoS (Array of Structures)
대량의 데이터를 처리할 때 데이터 구조체의 메모리 배치는 성능에 지대한 영향을 미친다. NvBlox는 GPU 성능 최적화를 위해 구조체의 배열(AoS) 대신 **배열의 구조체(SoA)**에 가까운 형태, 혹은 데이터 타입별로 분리된 블록 구조를 지향한다.
- AoS 방식:
struct Voxel { float distance; float weight; Color c; }와 같이 정의하고 이들의 배열을 만든다. 이 경우, 거리(distance) 값만 업데이트하는 커널을 실행할 때도 불필요한 색상(Color) 데이터가 캐시 라인에 함께 딸려 들어와 메모리 대역폭을 낭비하게 된다. - SoA 방식: NvBlox는 이를 분리하여
Block내부에float distances,float weights와 같이 데이터를 배열별로 모아둔다. 혹은 아예TSDF Layer,Color Layer와 같이 레이어 자체를 분리한다.
이러한 구조는 특정 연산(예: TSDF 통합, Raycasting)에 필요한 데이터만 밀집시켜 로드할 수 있게 하여, 제한된 GPU 메모리 대역폭(Memory Bandwidth)을 100% 활용할 수 있게 한다. 특히 Jetson과 같은 임베디드 GPU는 데스크톱 GPU에 비해 대역폭이 좁기 때문에 이러한 최적화가 필수적이다.
3. GPU 메모리 접근 패턴과 최적화: 코알레싱(Coalescing)의 마법
희소 복셀 그리드 구조가 아무리 이론적으로 훌륭해도, 실제 하드웨어상에서의 메모리 접근 패턴이 비효율적이라면 무용지물이다. NvBlox가 기존의 CPU 기반 방식(Voxblox 등)이나 옥트리 기반 방식(OctoMap)을 압도하는 성능을 낼 수 있는 비결은 **메모리 코알레싱(Memory Coalesced Access)**의 원칙을 철저히 준수하기 때문이다.
3.1 글로벌 메모리 접근의 물리학
GPU의 글로벌 메모리(Global Memory)는 대용량이지만 접근 속도(Latency)가 느리다. 따라서 메모리 대역폭을 효율적으로 사용하는 것이 성능 최적화의 제1원칙이다. NVIDIA GPU에서 글로벌 메모리 접근은 32바이트, 64바이트, 또는 128바이트 단위의 트랜잭션(Transaction)으로 이루어진다.8
예를 들어, 워프 내의 32개 스레드가 각자 4바이트(float) 데이터를 읽는다고 가정하자.
- 비병합 접근(Uncoalesced Access): 스레드들이 서로 멀리 떨어진 주소(Strided Access)나 무작위 주소에 접근한다면, GPU 메모리 컨트롤러는 32개의 데이터를 가져오기 위해 32번의 개별적인 메모리 트랜잭션을 수행해야 한다. 이는 버스 대역폭의 심각한 낭비를 초래하며, 유효 대역폭을 1/32로 떨어뜨린다. 옥트리 구조에서 포인터를 따라 노드를 탐색하는 과정이 전형적인 비병합 접근의 예다.
- 병합 접근(Coalesced Access): 32개의 스레드가 연속된 주소(k, k+1,..., k+31)에 접근한다면, GPU는 이를 단 1~2번의 128바이트 트랜잭션으로 묶어서 처리할 수 있다. 이는 고속도로에서 버스 한 대에 32명을 태워 보내는 것과 각자 자가용을 타고 가는 것의 차이와 같다.
3.2 NvBlox에서의 적용: 선형화(Linearization)
NvBlox는 VoxelBlock 내부의 복셀 인덱싱을 선형화하여 코알레싱을 강제한다. 3차원 인덱스 (x, y, z) (여기서 0 \le x, y, z < 8)는 다음과 같이 1차원 인덱스로 변환되어 메모리에 저장된다.
\text{Index}_{1D} = x + y \cdot 8 + z \cdot 8^2
TSDF 통합(Integration)이나 레이캐스팅(Raycasting) 커널이 실행될 때, 스레드 블록은 이 1차원 인덱스에 맞춰 할당된다. 즉, 스레드 ID t는 복셀 인덱스 v, 스레드 ID t+1은 복셀 인덱스 v+1을 처리하도록 설계된다. 블록 내의 데이터가 이미 연속적으로 저장되어 있으므로, 이러한 스레드 할당은 자연스럽게 완벽한 병합 접근을 유도한다.
이것이 바로 NvBlox가 옥트리 대신 블록 해싱을 선택한 결정적인 하드웨어적 이유다. 옥트리는 메모리 효율은 좋지만, 데이터가 메모리 여기저기에 흩어져 있어(Fragmented) GPU의 코알레싱 메커니즘을 활용할 수 없다. 반면 NvBlox는 “블록 단위의 희소성“과 “블록 내부의 밀집성“을 결합하여, 희소성의 이점을 취하면서도 GPU 하드웨어가 가장 좋아하는 선형 메모리 접근 패턴을 유지한다.1
4. 데이터 저장과 레이어(Layer) 시스템: 확장 가능한 ‘LayerCake’
NvBlox는 단일 맵에 모든 정보를 섞어서 담는 대신, **레이어(Layer)**라는 모듈형 개념을 도입하여 데이터를 관리한다. 개발자들 사이에서 “LayerCake“라고 불리는 이 구조는 서로 다른 유형의 데이터를 독립적인 레이어로 관리하되, 동일한 공간 좌표계와 블록 구조를 공유하게 한다.3
4.1 레이어 아키텍처의 유연성
모든 레이어는 동일한 해시 테이블 키(블록 인덱스)를 공유한다. 즉, 공간상 좌표 P에 대한 TSDF 값을 조회한 후, 동일한 인덱스를 사용하여 즉시 Color 레이어나 ESDF 레이어의 값에 접근할 수 있다. 이러한 ‘Co-located(동일 위치)’ 및 ‘Aligned(정렬된)’ 특성은 센서 퓨전(Sensor Fusion)을 매우 단순하고 효율적으로 만든다.11
예를 들어, 컬러 입혀진 메쉬(Colored Mesh)를 생성한다고 가정하자. 시스템은 TSDF 레이어에서 표면의 기하학적 형상을 추출하고, 동일한 좌표를 이용해 Color 레이어에서 색상 정보를 가져와 텍스처를 입힌다. 별도의 좌표 변환이나 복잡한 매칭 과정이 필요 없다.
4.2 주요 레이어 상세 명세
| 레이어 이름 | 데이터 타입 | 용도 및 특징 | 메모리 효율 (Byte/Voxel) |
|---|---|---|---|
| TSDF Layer | float (Distance), float (Weight) | 표면 재구성을 위한 핵심 레이어. 노이즈 제거 및 평활화 효과. | 8 bytes (Basic) / 4 bytes (Half) |
| ESDF Layer | float (Distance) | 장애물 회피 및 경로 계획용. 로봇 본체와의 충돌 거리를 나타냄. | 4 bytes |
| Color Layer | struct Color (R, G, B, A) | 시각화 및 시멘틱 분석용. | 4 bytes (RGBA8) |
| Occupancy Layer | uint8 (Probability) | 동적 물체(사람 등) 표현 및 2D 내비게이션 맵 생성. | 1 byte |
| Mesh Layer | Block 단위의 Vertex/Index 버퍼 | 시각화용 삼각형 메쉬 데이터. 복셀이 아닌 블록 단위로 관리됨. | Variable |
[표 2] NvBlox의 주요 레이어 구성 및 데이터 명세
- TSDF (Truncated Signed Distance Function): 각 복셀은 가장 가까운 표면까지의 부호 있는 거리(Signed Distance)를 저장한다. 표면 앞쪽은 양수, 뒤쪽은 음수, 표면 자체는 0(Zero-crossing)으로 표현된다. ’Truncated’는 표면 근처의 일정 범위(-\mu \le d \le \mu) 내에서만 정확한 값을 저장하고, 그 외에는 절단된 값을 저장하여 정보 압축 및 계산 효율을 높인다는 의미다.
- ESDF (Euclidean Signed Distance Field): TSDF는 표면 근처의 정보만 가지므로 경로 계획에는 부족하다. ESDF는 이를 확장하여 공간 상의 모든 복셀에 대해 가장 가까운 장애물까지의 유클리드 거리를 저장한다. NvBlox는 TSDF로부터 ESDF를 GPU 상에서 병렬로 고속 생성(Propagation)하는 알고리즘을 탑재하여, 기존 CPU 방식 대비 최대 31배 빠른 속도를 달성했다.3
4.3 사용자 정의 레이어의 확장성
NvBlox의 강력한 점은 사용자가 자신만의 커스텀 레이어를 손쉽게 추가할 수 있다는 것이다. 예를 들어 ’WiFi 신호 강도 레이어’나 ’방사능 수치 레이어’가 필요하다면, 사용자는 단지 struct Voxel의 내용을 정의하기만 하면 된다. NvBlox의 템플릿 메타프로그래밍(Template Metaprogramming) 엔진이 컴파일 타임에 해당 레이어를 위한 GPU 커널, 메모리 관리 코드, 직렬화 코드를 자동으로 생성한다.3 이는 연구자와 개발자가 하위 레벨의 CUDA 코드를 건드리지 않고도 고성능 매핑 시스템을 확장할 수 있게 해준다.
5. 비교 분석: NvBlox vs. OctoMap vs. Voxblox
이 절에서는 NvBlox의 희소 복셀 그리드가 기존의 대표적인 매핑 솔루션들과 비교하여 어떤 구조적 차이와 성능 우위를 가지는지 정량적, 정성적으로 분석한다.
| 비교 항목 | NvBlox (Sparse Voxel Grid) | OctoMap (Octree) | Voxblox (CPU Hash Block) |
|---|---|---|---|
| 자료 구조 | 2-Level Block Hashing (GPU Optimized) | Hierarchical Tree (8-child recursive) | 2-Level Block Hashing (CPU Optimized) |
| 인덱싱 시간 복잡도 | O(1) (Amortized) | O(\log N) (Tree Depth에 비례) | O(1) (Amortized) |
| 메모리 접근 패턴 | 병합 접근 (Coalesced), 예측 가능 | 비병합 접근 (Pointer Chasing), 랜덤 | 캐시 친화적이나 직렬 처리 한계 |
| GPU 가속 적합성 | 최상 (분기 최소화, SIMT 구조) | 하 (스레드 분기 심화, 스택 오버헤드) | 중/하 (데이터 전송 병목 발생) |
| 데이터 확장성 | stdgpu를 통한 동적 리사이징 지원 | 트리 노드 동적 추가 | std::unordered_map 확장 |
| 표면 재구성 속도 | 기준 (CPU 대비 최대 177배) | 느림 (Occupancy 기반, 낮은 정밀도) | NvBlox 대비 수십 배 느림 |
| 주요 활용처 | 고속 자율주행, 실시간 3D 경로 계획 | 정적 환경의 CPU 기반 매핑 | 드론(MAV)의 온보드 CPU 매핑 |
[표 3] 매핑 프레임워크별 데이터 구조 및 성능 비교 분석 3
5.1 OctoMap과의 비교: 구조적 한계의 극복
OctoMap은 오랫동안 로봇 매핑의 표준이었다. 그러나 OctoMap의 옥트리 구조는 GPU와 상극이다. 옥트리를 탐색하려면 루트 노드에서 시작하여 자식 노드의 포인터를 계속 따라가야 한다. 이는 GPU 스레드마다 서로 다른 메모리 위치를 참조하게 만들어 메모리 대역폭을 낭비하고, 스레드 간 실행 경로가 달라지는 **워프 분기(Warp Divergence)**를 유발한다. NvBlox의 해시 기반 접근은 한 번의 수식 계산으로 블록 위치를 찾아내므로 이러한 분기 문제를 원천적으로 차단한다.
5.2 Voxblox과의 비교: 아키텍처의 진화
Voxblox는 NvBlox의 직계 조상 격인 라이브러리로, NvBlox와 유사한 블록 해싱 구조를 가지고 있다. 그러나 Voxblox는 CPU 실행을 전제로 설계되었다. CPU에서 계산한 데이터를 GPU로 옮겨서 시각화하거나 경로 계획에 사용하려면 PCIe 버스를 통한 데이터 전송이 병목이 된다. NvBlox는 **“모든 것을 GPU에서(Everything on GPU)”**라는 철학 아래, 데이터의 생성, 관리, 조회, 삭제까지 모든 수명 주기를 GPU 메모리 안에서 처리한다. 이로 인해 CPU 부하를 획기적으로 낮추고 전체 시스템의 처리량을 극대화했다.
6. 동적 환경 대응과 메모리 관리 전략
실제 로봇 환경은 정적이지 않다. 사람이 걸어 다니고, 물건이 옮겨지며, 로봇은 끊임없이 새로운 영역을 탐험한다. 희소 복셀 그리드는 이러한 동적 환경 관리에도 탁월한 이점을 제공한다.
6.1 동적 객체 분리 및 처리 (Dynamic Object Handling)
NvBlox는 입력된 RGB-D 이미지에서 딥러닝 모델(Isaac ROS Image Segmentation 등)을 이용해 사람과 같은 동적 객체를 픽셀 단위로 분할(Segmentation)한다. 희소 그리드 구조 덕분에, 사람으로 판별된 영역의 깊이(Depth) 데이터는 정적 맵(Static Map)인 TSDF 레이어에 통합되지 않고, 별도의 **‘사람 점유 레이어(People Occupancy Layer)’**로 분기되어 저장된다.13
이때, 사람이 이동하면 이전 위치의 점유 확률은 빠르게 감소(Decay)시키고 새로운 위치의 블록을 즉시 할당한다. 블록 단위의 관리 덕분에 맵 전체를 다시 계산할 필요 없이, 해당 객체가 점유한 소수의 블록만 업데이트하면 되므로 매우 효율적이다.
6.2 메모리 풀(Memory Pool)과 스트리밍
아무리 효율적인 구조라도 GPU 메모리는 유한하다(특히 8GB~16GB 수준의 Jetson Orin 등에서는 더욱 그렇다). NvBlox는 메모리 고갈을 막기 위해 **블록 메모리 풀(Block Memory Pool)**과 순환 버퍼(Circular Buffer) 개념을 도입했다.7
- 블록 재사용(Recycling): 더 이상 관측되지 않거나 로봇으로부터 너무 멀어진 블록, 혹은 동적 객체가 사라져 빈 공간이 된 블록은 메모리를 해제하는 대신 ’가용 블록 리스트(Free List)’로 돌아간다. 새로운 데이터가 들어오면 운영체제에
malloc을 요청하는 대신 이 리스트에서 블록을 꺼내 쓴다. 이는 잦은 메모리 할당/해제로 인한 성능 저하(Fragmentation 및 Overhead)를 방지한다. - 맵 스트리밍 및 스와핑: 로봇이 대규모 환경을 탐사할 때, GPU 메모리가 가득 차면 로봇 반경 밖의 오래된 블록 데이터를 호스트(CPU) 메모리로 내리고(Swap-out), GPU 메모리 공간을 확보한다. 이 과정은 백그라운드에서 비동기적으로 이루어져 매핑 성능에 영향을 주지 않는다.
7. 결론: NvBlox 데이터 구조의 의의
NvBlox의 희소 복셀 그리드 구조는 단순한 데이터 저장소가 아니다. 이는 **‘병렬 처리 효율성(Parallel Efficiency)’**과 **‘공간 표현 효율성(Spatial Efficiency)’**이라는, 종종 상충하는 두 가지 공학적 목표를 동시에 달성하기 위해 하드웨어 레벨부터 소프트웨어 아키텍처까지 정교하게 조율된 기술의 결정체다.
- 공간 해싱을 통해 O(1)의 접근 속도와 무한에 가까운 확장성을 확보했다.
- 8 \times 8 \times 8 블록 구조를 통해 GPU 워프 스케줄링과 캐시 적중률을 극한으로 끌어올렸다.
- 메모리 코알레싱을 강제하여 메모리 대역폭이라는 물리적 한계를 극복했다.
- 레이어 아키텍처를 통해 다중 센서 융합과 미래의 확장성을 보장했다.
이러한 견고한 기반 위에서 NvBlox는 CPU 기반 방식 대비 수십 배에서 수백 배 빠른 재구성 속도를 입증했다. 이는 고속으로 비행하는 드론이 충돌 없이 숲속을 누비거나, 자율주행 로봇이 복잡한 도심 인파 속에서 안전하게 경로를 찾는 미래를 현실로 만드는 핵심 기술이다. 2.1.1절에서 살펴본 이 희소 복셀 그리드 구조에 대한 이해는, 단순히 NvBlox 라이브러리의 사용법을 익히는 것을 넘어, GPU 가속 로보틱스라는 새로운 패러다임의 본질을 꿰뚫는 첫걸음이 될 것이다.
8. 참고 자료
- Are sparse voxel octrees really (always) the best voxel data structure? : r/VoxelGameDev, https://www.reddit.com/r/VoxelGameDev/comments/1jb8uol/are_sparse_voxel_octrees_really_always_the_best/
- Why are oct trees so much more common than hash tables?, https://computergraphics.stackexchange.com/questions/8364/why-are-oct-trees-so-much-more-common-than-hash-tables
- nvblox: GPU-Accelerated Incremental Signed Distance Field Mapping - arXiv, https://arxiv.org/html/2311.00626v2
- nvblox: GPU-Accelerated Incremental Signed Distance Field Mapping | Request PDF, https://www.researchgate.net/publication/382991572_nvblox_GPU-Accelerated_Incremental_Signed_Distance_Field_Mapping
- nosferalatu/SimpleGPUHashTable: A simple GPU hash table implemented in CUDA using lock free techniques - GitHub, https://github.com/nosferalatu/SimpleGPUHashTable
- Maximizing Performance with Massively Parallel Hash Maps on GPUs - NVIDIA Developer, https://developer.nvidia.com/blog/maximizing-performance-with-massively-parallel-hash-maps-on-gpus/
- Nvblox quickstart example crashing - Isaac ROS - NVIDIA Developer Forums, https://forums.developer.nvidia.com/t/nvblox-quickstart-example-crashing/333188
- Unlock GPU Performance: Global Memory Access in CUDA | NVIDIA Technical Blog, https://developer.nvidia.com/blog/unlock-gpu-performance-global-memory-access-in-cuda/
- Coalesced Memory Access in CUDA for High-Performance Computing - Victor Leung, https://victorleungtw.wordpress.com/2025/01/16/coalesced-memory-access-in-cuda-for-high-performance-computing/
- To Octree or not to octree? : r/VoxelGameDev - Reddit, https://www.reddit.com/r/VoxelGameDev/comments/hsyrgd/to_octree_or_not_to_octree/
- Technical Details - NVIDIA Isaac ROS, https://nvidia-isaac-ros.github.io/concepts/scene_reconstruction/nvblox/technical_details.html
- Incremental Multimodal Surface Mapping via Self-Organizing Gaussian Mixture Models, https://www.ri.cmu.edu/app/uploads/2023/10/main.pdf
- nvblox_ros1/docs/technical-details.md at main - GitHub, https://github.com/ethz-asl/nvblox_ros1/blob/main/docs/technical-details.md
- Build High Performance Robotic Applications with NVIDIA Isaac ROS Developer Preview 3, https://developer.nvidia.com/blog/build-high-performance-robotic-applications-with-nvidia-isaac-ros-developer-preview-3/
- What’s the realistic maximum size this can run on an Orin? · Issue #96 · NVIDIA-ISAAC-ROS/isaac_ros_nvblox - GitHub, https://github.com/NVIDIA-ISAAC-ROS/isaac_ros_nvblox/issues/96